package com.gmail.hy87cn.hellohi.client;

import com.gmail.hy87cn.hellohi.shared.ERoomState;
import com.gmail.hy87cn.hellohi.shared.HiMessage;
import com.gmail.hy87cn.hellohi.shared.HiRoom;
import com.gmail.hy87cn.hellohi.shared.HiUpdateException;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;

/**
 * @author HeYi
 * 
 */
public class HiChatClient {
	private int token = -1;
	private HiRoom room;
	private HiChatUi ui;
	private final HiServiceAsync service = GWT.create(HiService.class);
	private static final int PERIOD = 2000;
	private final Timer timer = new Timer() {
		@Override
		public void run() {
			if (token >= 0) {
				if (room != null && room.getState() == ERoomState.Locked) {
					updateMessages();
				} else {
					synchronize();
				}
			}
		}
	};

	public int getToken() {
		return token;
	}

	public HiRoom getRoom() {
		return room;
	}

	public HiChatClient() {
		initClientId();
	}

	public void setUi(HiChatUi ui) {
		this.ui = ui;
	}

	private void initClientId() {
		service.getToken(new AsyncCallback<Integer>() {
			@Override
			public void onFailure(Throwable caught) {
				initClientId();
			}

			@Override
			public void onSuccess(Integer result) {
				token = result;
			}
		});
	}

	private int lastIndex = 0;

	private void updateMessages() {
		service.updateMessages(token, lastIndex,
				new AsyncCallback<HiMessage[]>() {
					@Override
					public void onFailure(Throwable caught) {
						if (caught.getClass() == HiUpdateException.class) {
							synchronize();
						}
					}

					@Override
					public void onSuccess(HiMessage[] result) {
						if (result != null && result.length != 0) {
							ui.onMessageUpdated(result);
							lastIndex += result.length;
						}
					}
				});
	}

	private String sendingMessage;

	public void sendMessage(String input) {
		sendingMessage = input;
		service.sendMessage(token, input, new AsyncCallback<Void>() {
			@Override
			public void onFailure(Throwable caught) {
				ui.onMessageSendFailed(sendingMessage);
			}

			@Override
			public void onSuccess(Void result) {
				lastIndex++;
			}
		});
	}

	public void connect() {
		service.connect(token, null, new AsyncCallback<Void>() {
			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub
			}

			@Override
			public void onSuccess(Void result) {
				synchronize();
				timer.scheduleRepeating(PERIOD);
			}
		});
	}

	public void disconnect() {
		service.disconnect(token, new AsyncCallback<Void>() {

			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub
			}

			@Override
			public void onSuccess(Void result) {
				room = null;
				ui.onRoomUpdated(null);
			}
		});
	}

	public void switchState() {
		if (room == null) {
			connect();
		} else if (room.getState() == ERoomState.Locked) {
			disconnect();
		}
	}

	public void releaseToken() {
		service.releaseToken(token, new AsyncCallback<Void>() {

			@Override
			public void onFailure(Throwable caught) {
				// if failed, the token will be release by the pulse mechanism
			}

			@Override
			public void onSuccess(Void result) {
				timer.cancel();
			}
		});
	}

	private void synchronize() {
		service.getRoom(token, new AsyncCallback<HiRoom>() {

			@Override
			public void onFailure(Throwable caught) {
				// TODO Auto-generated method stub
			}

			@Override
			public void onSuccess(HiRoom result) {
				room = result;
				ui.onRoomUpdated(room);
			}

		});
	}
}
